use axum::extract::{Path, State};
use axum::http::Response;
use axum::response::IntoResponse;
use axum::Json;
use axum::{body::Body, extract::Query};
use redis::AsyncCommands;
use serde_json::json;
use sky_pojo::dto;
use sky_pojo::vo::dish::DishVO;
use sky_pojo::vo::PageResult;

use crate::bootstrap::AppState;
use crate::middleware::jwt::admin::Claims;
use crate::router::resp::RespVO;
use crate::service;
use sky_pojo::{dto::dish::DishDTO, entities::dish::Model};

pub async fn create_dish(
    claim: Claims,
    State(state): State<AppState>,
    Json(dto): Json<DishDTO>,
) -> Response<Body> {
    tracing::info!("new dish: {:?}", dto);

    clean_cache(state.redis, format!("dish_{:?}", dto.id)).await;
    match service::dish::save_with_flavor(state.db_pool, claim.id, dto).await {
        Ok(id) => RespVO::<i64>::success(format!("success"), Some(id)).into_response(),
        Err(e) => RespVO::<()>::error_without_data(e).into_response(),
    }
}

pub async fn modify_dish(
    claim: Claims,
    State(state): State<AppState>,
    Json(dto): Json<DishDTO>,
) -> Response<Body> {
    tracing::info!("{} is modifing dish: {:?}", claim.id, dto);

    match service::dish::modify(state.db_pool, dto, claim.id).await {
        Ok(model) => {
            clean_cache(state.redis, "dish_*".to_owned()).await;
            RespVO::success("success".to_owned(), Some(model)).into_response()
        }
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

pub async fn enable_disable(
    claim: Claims,
    State(state): State<AppState>,
    Path(status): Path<i32>,
    Json(id): Json<i64>,
) -> Response<Body> {
    match service::dish::enable_disable(state.db_pool, id, status, claim.id).await {
        Ok(model) => {
            clean_cache(state.redis, "dish_*".to_owned()).await;
            RespVO::<Model>::success("success".to_owned(), Some(model)).into_response()
        }
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

pub async fn delete_by_ids(
    claim: Claims,
    State(state): State<AppState>,
    Query(ids): Query<Vec<i64>>,
) -> Response<Body> {
    tracing::info!("{} is deleting dish by id: {:?}", claim.id, ids);

    match service::dish::delete_by_ids(state.db_pool, ids).await {
        Ok(res) => {
            clean_cache(state.redis, "dish_*".to_owned()).await;
            let rows_affected = res.rows_affected;
            RespVO::<serde_json::Value>::success(
                "delete success".to_owned(),
                Some(json!({
                    "rows_affected": rows_affected
                })),
            )
            .into_response()
        }
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

pub async fn find_by_id(
    _claim: Claims,
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> Response<Body> {
    match service::dish::find_by_id_with_flavor(state.db_pool, id).await {
        Ok(vo) => RespVO::<DishVO>::success("success".to_owned(), Some(vo)).into_response(),
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

pub async fn find_by_category(
    _claim: Claims,
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> Response<Body> {
    match service::dish::find_by_category(state.db_pool, id).await {
        Ok(model) => {
            RespVO::<Vec<Model>>::success("success".to_owned(), Some(model)).into_response()
        }
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

pub async fn query(
    _claim: Claims,
    State(state): State<AppState>,
    Query(dto): Query<dto::dish::DishPageQueryDTO>,
) -> Response<Body> {
    match service::dish::query(state.db_pool, dto).await {
        Ok(pages) => {
            RespVO::<PageResult<Model>>::success("success".to_owned(), Some(pages)).into_response()
        }
        Err(e) => RespVO::<()>::error_without_data(e.to_string()).into_response(),
    }
}

async fn clean_cache(mut redis: redis::aio::MultiplexedConnection, pattern: String) {
    let keys: Vec<String> = redis.keys(pattern).await.unwrap();

    // Delete the keys
    if !keys.is_empty() {
        redis.del::<Vec<String>, ()>(keys).await.unwrap();
    }
}
